package com.bang.study.day02.tree;



import java.util.*;

/**
 * @Auther: Bang
 * @Date: 2019/9/11 0011 18:03
 * @Description: 二叉搜索树
 */
public class BinarySearchTree<E extends Comparable<E>> {

    // 根节点
    private Node root;

    // 元素个数
    private int size;

    public BinarySearchTree() {
        this.root = null;
        this.size = 0;
    }

    public int getSize() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 添加元素
     *
     * @param e
     */
    public void add(E e) {
        root = add(root, e);
    }

    private Node add(Node node, E e) {
        if (node == null) {
            size++;
            node = new Node(e);
        } else {
            if (e.compareTo(node.value) > 0) {
                node.right = add(node.right, e);
            } else if (e.compareTo(node.value) < 0) {
                node.left = add(node.left, e);
            }
        }
        return node;
    }

    /**
     * 树是否包含E
     *
     * @param e
     * @return
     */
    public boolean contains(E e) {
        return contains(root, e);
    }

    private boolean contains(Node node, E e) {
        if (node == null) {
            return false;
        } else {
            E value = node.value;
            if (e.compareTo(value) == 0) {
                return true;
            } else if (e.compareTo(value) < 0) {
                return contains(node.left, e);
            } else {
                return contains(node.right, e);
            }
        }
    }

    /**
     * 前序遍历(先根->左子树->右子树)
     */
    public void preOrder() {
        preOrder(root);
    }

    private void preOrder(Node node) {
        if (node == null) {
            return;
        } else {
            System.out.println(node.value);
            preOrder(node.left);
            preOrder(node.right);
        }
    }


    /**
     * 中序遍历 左子树->根->右子树
     */
    public void inOrder() {
        inOrder(root);
    }

    private void inOrder(Node node) {
        if (node == null) {
            return;
        } else {
            inOrder(node.left);
            System.out.println(node.value);
            inOrder(node.right);
        }
    }

    /**
     * 后序遍历 左子树->右子树->根
     */
    public void postOrder() {
        postOrder(root);
    }

    private void postOrder(Node node) {
        if (null == node) {
            return;
        } else {
            postOrder(node.left);
            postOrder(node.right);
            System.out.println(node.value);
        }
    }

    /**
     * 先序遍历(非递归,基于栈)
     */
    public void preOrderNR() {
        Stack<Node> stack = new Stack<>();
        if (root != null) {
            stack.push(root);
            while (!stack.empty()) {
                Node curr = stack.pop();
                System.out.println(curr.value);
                if (curr.right != null) {
                    stack.push(curr.right);
                }
                if (curr.left != null) {
                    stack.push(curr.left);
                }
            }
        } else {
            System.out.println("BinarySearchTree is empty");
        }

    }

    /**
     * 层序遍历(基于队列)
     */
    public void levelOrder() {
        if (root == null) {
            System.out.println("BinarySearchTree is empty");
        } else {
            LinkedList<Node> queue = new LinkedList<>();
            queue.add(root);
            while (!queue.isEmpty()) {
                Node curr = queue.remove();
                System.out.println(curr.value);
                if (curr.left != null) {
                    queue.add(curr.left);
                }
                if (curr.right != null) {
                    queue.add(curr.right);
                }
            }
        }
    }

    /**
     * 返回树中的最小值
     *
     * @return
     */
    public E minimum() {
        if (isEmpty()) {
            throw new IllegalArgumentException("BinarySearchTree is empty");
        }
        Node minimum = minimum(root);
        return minimum.value;

    }

    private Node minimum(Node node) {
        if (node.left == null) {
            return node;
        } else {
            return minimum(node.left);
        }
    }

    /**
     * 查找树中的最大值
     *
     * @return
     */
    public E maximum() {
        if (isEmpty()) {
            throw new IllegalArgumentException("BinarySearchTree is empty");
        }
        Node maximum = maximum(root);
        return maximum.value;
    }

    private Node maximum(Node node) {
        if (node.right == null) {
            return node;
        } else {
            return maximum(node.right);
        }
    }


    /**
     * 删除树中的最大元素
     *
     * @return 返回最大元素
     */
    public E removeMax() {
        E ret = maximum();
        root = removeMax(root);
        return ret;
    }

    /**
     * 删除最大的元素节点
     *
     * @param node
     * @return
     */
    private Node removeMax(Node node) {
        if (node.right == null) {
            Node nodeLeft = node.left;
            node.left = null;
            size--;
            return nodeLeft;
        } else {
            node.right = removeMax(node.right);
            return node;
        }
    }


    /**
     * 删除最小的元素节点
     *
     * @return
     */
    public E removeMin() {
        E ret = minimum();
        root = removeMin(root);
        return ret;
    }

    private Node removeMin(Node node) {
        if (node.left == null) {
            Node nodeRight = node.right;
            node.right = null;
            size--;
            return nodeRight;
        } else {
            node.left = removeMin(node.left);
            return node;
        }
    }

    /**
     * 从二叉搜索树中删除元素为e的节点
     *
     * @param e
     */
    public void remove(E e) {
        if (e == null) {
            throw new IllegalArgumentException("e can not be null");
        }
        if (size == 0) {
            throw new IllegalArgumentException("BinarySearchTree is empty");
        }
        root = remove(root, e);

    }

    /**
     * 删除以node为根的二叉搜索树中值为e的节点，递归算法
     *
     * @param node
     * @param e
     * @return 删除节点后更新的二叉搜索树的根
     */
    private Node remove(Node node, E e) {
        if (node == null) {
            // 递归出口 未找到和e相等的元素
            return null;
        } else {
            E value = node.value;
            if (e.compareTo(value) > 0) {
                // 被删除元素e大于当前节点值e
                node.right = remove(node.right, e);
            } else if (e.compareTo(value) < 0) {
                //被删除元素e小于当前节点值e
                node.left = remove(node.left, e);
            } else {
                // 递归出口 被删除元素e等于当前节点值e
//                e.compareTo(e)==0
                //待删除节点左子树为空情况
                if (node.left == null) {
                    Node rightNode = node.right;
                    node.right = null;
                    size--;
                    return rightNode;

                } else if (node.right == null) {
                    //待删除节点右子树为空情况
                    Node leftNode = node.left;
                    node.left = null;
                    size--;
                    return leftNode;

                } else {
                    // 待删除节点左右子树均不为空,此时需要找到此节点的后驱,用后驱去顶替当前节点
                    // 找到此节点的后驱(即当前节点右子树中最小的那个叶子节点) successor
                    Node successor = minimum(node.right); // 找到右子树中的最小元素,替换当前node
                    successor.right = removeMin(node.right); //  删除右子树中的最小元素,并将删除后的结果作为后驱的右子树
                    successor.left = node.left; // 将node的左子树作为后驱的左子树
                    node.right = node.left = null; // 将node置为空 方便gc
                    return successor;
                }
            }
            return node;
        }
    }


    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        printTreeStr(root, 0, sb);
        return sb.toString();
    }

    /**
     * 打印树
     *
     * @param root  根节点
     * @param depth 当前节点的深度
     * @param sb
     */
    private void printTreeStr(Node root, int depth, StringBuilder sb) {
        if (null == root) {
            sb.append(generateDepthString(depth) + "null\n");
            return;
        } else {
            E value = root.value;
            sb.append(generateDepthString(depth) + value + "\n");
            printTreeStr(root.left, depth + 1, sb);
            printTreeStr(root.right, depth + 1, sb);
        }
    }

    private String generateDepthString(int depth) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < depth; i++) {
            sb.append("--");
        }
        return sb.toString();
    }

    private class Node {

        public Node left;

        public E value;

        public Node right;

        public Node() {
        }

        public Node(E e) {
            this.value = e;
            this.left = null;
            this.right = null;
        }

        public Node(Node left, E value, Node right) {
            this.left = left;
            this.value = value;
            this.right = right;
        }
    }


}
